<?php
namespace app\manage\service;

use app\manage\service\ApiSuper;
/**
 * 用于app发送过来的各种参数的调度服务用途
 * @author 李全春 <adv30@126.com>  <time:2016年4月17日19时44分39秒>
 */
class Api extends ApiSuper{

    private $ver;         //当前要调度的版本
    private $appkey;      //用传输的字串拼接后的MD5值

    private $diffTime;    //从手机发出的时间戳与服务器的时间戳的差的绝对值;
    private $module;      //要进入的模块
    private $controller;  //要执行的类
    private $action;      //要执行的动作

    private $filePath;    //controller类所在的文件路径
    private $obj;         //new controller 后得到的对象

    private $verArr;      //允许访问的版本目录, 在Conf/config.php中配置 
    private $moduleArr;   //允许访问的模块目录 trade云交易 logistics云物流 在Conf/config.php中配置
    private $checkActionArr; //需要检查的参数 在Conf/config.php中配置

    public function __construct()
    {
        $this->_init();         // 接收参数
        $this->_checkParam();   // 验证信息是否被改过..参数是否正确等等
        //判断是否有错误信息
        if( 'noErr' !== $this->err ){
            return;
        }
        //判断当前收到的数据是否是加密过的,如果是,则解密,解密部分现在还有问题,没有完成
        if( 'on' === $this->encryption ){
            $this->operation = 'DECODE';  //进行解密
            $this->textData  = $this->authCode( $this->textData, $this->operation, $this->key, $this->expiry );
        }
        // 生成要调用的对象,实例化的对象都要是apiBase类的子类
        $controller = '\\app\\' . $this->ver . '\\service\\' . $this->module . '\\' . $this->controller;
        $this->obj = new $controller( $this->textData, $this->fileData, $this->encryption );
        // $this->obj = service('app\serveruser\user\ApiUser');
        //检查方法是否可被调用
        $this->_checkCallable();
        if( 'noErr' != $this->err ){
            return;
        }
    }

    /**
     * 初始化接收各种参数
     * @param  string $ver 要调用的目录
     */
    private function _init()
    {
        //上线时接收参数要过滤,textData会在其他地方过滤
        $this->ver        = input('post.ver');               //要调用的版本
        $this->sessionId  = session_id();               //访问者的sessionId
        $this->appkey     = input('post.appkey');           //用传输的字串拼接后的MD5值
        $this->sendTime   = substr( input('post.sendTime'), 0, 10 );    //发送方发送时的时间戳,秒级别 10位
        $this->diffTime   = 3000;                       //设置服务器的默认误差时间
        $this->time       = NOW;                        //服务器的时间
        $this->module     = input('post.module');           //要进入的模块
        // $this->dir        = input('post.dir');              //要进入的模块   目录

        $this->controller = input('post.controller');       //要执行的类
        $this->action     = input('post.action');           //要执行的动作
        $this->encryption = input('post.encryption');       //是否加密  两个值 on||off
        $this->textData   = input('post.textData','',false);     //接收到的文本数据
        $this->fileData   = input('files.');                    //接收到的文件数据

        $this->verArr     = array_keys(config('arrow_make_server'));
        $this->moduleArr  = config('arrow_module');
        $this->checkActionArr  = [
            '_checkSendTime',
            '_checkVer',
            '_checkAppkey',
            '_checkModule',
            '_checkFile',
            '_checkEncryption'
        ];
        $fileName = $this->controller . '.php';
        $this->filePath =  APP_PATH . $this->ver . '/service/'  . $this->module . '/' . $fileName;
    }


    // 对接收到的参数进行各种检查
    private function _checkParam()
    {
        foreach ($this->checkActionArr as $k => $checkAction) {
            if( 'noErr' == $this->err ){
                $this->$checkAction();
            }else{
                break;
            }
        }
    }

    //检查发送时间的误差
    private function _checkSendTime()
    {
        if( !$this->sendTime ){
            $this->_setErr( 'param', PARAM_ERR_STATUS );
        }elseif( abs($this->time - $this->sendTime) > $this->diffTime ) {
            $this->_setErr( 'time', TIME_ERR_STATUS );
        }
    }

    // 检查版本目录是否允许访问
    private function _checkVer()
    {
        if( !$this->ver ){
            $this->_setErr( 'param', PARAM_ERR_STATUS );
        }elseif( !in_array( $this->ver,$this->verArr ) ){
            $this->_setErr( 'ver' );
        }
    }

    // 检查appkey是否正常
    private function _checkAppkey()
    {
        if( !$this->appkey ){
            $this->_setErr( 'param', PARAM_ERR_STATUS );
        }

        // 针对不同的版本采用不同的加密方式
        switch( $this->ver ){
            case 'serveruser':
                // 调用了公共的签名规则
                $serviceKey = $this->signPublic( $this->sendTime, $this->ver, $this->encryption, $this->module, $this->controller, $this->action );
                break;

            default:
                // 调用了公共的签名规则
                $serviceKey = $this->signPublic( $this->sendTime, $this->ver, $this->encryption, $this->module, $this->controller, $this->action );
        }
        if( $this->appkey !== $serviceKey ){
            $this->_setErr( 'appkey' );
        }
    }

    //检查模块是否允许访问
    private function _checkModule()
    {
        if( !$this->module ){
            $this->_setErr( 'param', PARAM_ERR_STATUS );
        }elseif( !in_array($this->module, $this->moduleArr[$this->ver]) ){
            $this->_setErr( 'module' );
        }
    }

    //检检类文件是否存在
    private function _checkFile()
    {
        if( !file_exists($this->filePath) ){
            $this->_setErr( 'controller' );
        }
    }

    //检查action是否能被调用,此方法不可放在_checkParam中调用,因为对象还没有生成
    private function _checkCallable()
    {

        if( !$this->action ){
            $this->_setErr( 'param', PARAM_ERR_STATUS );
        }elseif( !is_callable(array($this->obj, $this->action)) ){
            $this->_setErr( 'action' );
        }
    }

    //检查encryption参数是否为on || off
    private function _checkEncryption()
    {
        if( 'on' != $this->encryption && 'off' != $this->encryption ){
            $this->_setErr('encryption');
        }
    }

    //设置run之前的错误信息
    private function _setErr($err, $status = ERR_STATUS)
    {
        $this->status = $status;
        $this->err    = $err;
        $this->msg    = $this->errInfoArr[$this->err];
    }

    // 运行需要执行的动作
    public function run(): array
    {
        if( 'noErr' != $this->err ){
            return $this->ajaxReturn();
        }
        $obj    = $this->obj;
        $action = $this->action;
        $res    = $obj->$action();
        $this->status = $res['status'];
        $this->code   = $res['code'];
        $this->msg    = $res['msg'];
        $this->res    = $res['res'];
        return $res;
    }
}
